#include "dtt_scene.h"
#include "resource_define.h"

static glm::vec3 gCameraPos(0.0f, 0.0f, 1.5f);
const float kPI = 3.1415926f;
const float angleStride = kPI / 90.0f;
const float radiusStride = 0.05f;

static float radius = 1.5f;
static float alpha = kPI / 2.0f;
static float theta = 0.0f;

void setCameraPos() 
{
    gCameraPos.x = radius * glm::cos(theta) * glm::cos(alpha);

    gCameraPos.y = radius * glm::sin(theta);

    gCameraPos.z = radius * glm::cos(theta) * glm::sin(alpha);
}

DTTScene::DTTScene()
    : _LengthThreshold(70.0f)
{
}


void DTTScene::init()
{
    using namespace DataSet1;
    try
    {
        float scale = kuWidth > kuHeight ? kuWidth : kuHeight;
        scale = scale > kuDepth ? scale : kuDepth;

        float hw = (float)kuWidth / 2.0f;
        float hh = (float)kuHeight / 2.0f;
        float hd = (float)kuDepth / 2.0f;

        glm::mat4 mat = glm::translate(-hw, -hh, -hd);
        mat = glm::scale(glm::vec3(2.0f/scale)) * mat;
        _modelViewMatrix = mat;

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        _shaderInit();
        _vboInit();
        _textureInit();
        _vao.bind();

    }
    catch(std::exception& e)
    {
        Log::error(e.what());
        throw;
    }
}


void DTTScene::onRender()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glm::mat4 mat = glm::lookAt(
        gCameraPos,
        glm::vec3(0, 0, 0),
        glm::vec3(0, 1, 0));

    glm::mat4 mv = mat /**_trackball.GetRotateMatrix()*/ * _modelViewMatrix;
    glm::mat4 nv = mv;

    glm::mat4 mvp = _projectionMatrix * mv;
    glm::vec2 windowing(DataSet1::kfWindowCenter, DataSet1::kfWindowWidth);

    _program.use();
    _vao.bind();
    _pIndexBuffer->bind();

    _program.setUniform("ModelViewMatrix", mv);
    //_program.setUniform("NormalViewMatrix", nv);
    _program.setUniform("ProjectionMatrix", _projectionMatrix);
    //_program.setUniform("ModelViewProjectionMatrix", mvp);
    _program.setUniform("LengthThreshold", _LengthThreshold);

    glDrawElements(GL_LINES_ADJACENCY, _pIndexBuffer->numberOfData(), GL_UNSIGNED_INT, (GLvoid*)NULL);

    _pIndexBuffer->unBind();
    _vao.unBind();
    _program.unuse();

}


void DTTScene::onKeyboard( unsigned char key, int x, int y )
{
    printf(Yellow, "%c %d, %d\n", key, x, y);
    float step = 1.0f;
    switch(key)
    {
    case '-':
        _LengthThreshold += step; break;
    case '=':
        _LengthThreshold -= step; break;
    case 'w':
        radius -= radiusStride; break;
    case 's':
        radius += radiusStride; break;
    }
    radius = glm::clamp(radius, 0.1f, 10.0f);
    setCameraPos();

    glutPostRedisplay();
}

void DTTScene::onMouseMove( int x, int y )
{
    _trackball.endPoint(glm::vec2(x, y));
    //glm::vec2 next(x, y);
    //glm::vec2 diff = next - point;
    //if (diff.y < -5)
    //{
    //    windowing.x += 2.0f;
    //}
    //if (diff.y > 5)
    //{
    //    windowing.x -= 2.0f;
    //}

    //if (diff.x < -5)
    //{
    //    windowing.y -= 10.0f;
    //}
    //if (diff.x > 5)
    //{
    //    windowing.y += 10.0f;
    //}

    //point = next;
    glutPostRedisplay();
}

void DTTScene::onMouseButtonEvent( int button, int state, int x, int y )
{
    if ((GLUT_LEFT_BUTTON == button) && (GLUT_DOWN == state))
    {
        _trackball.startPoint(glm::vec2(x, y));
    }
}

void DTTScene::onSizeChanged( int iWidth, int iHeight )
{
    _trackball.setBound(0, 0, iWidth, iHeight);
    Scene::onSizeChanged(iWidth, iHeight);
}

void DTTScene::_shaderInit()
{
    //    _program.addShaderFile(GPUProgram::Vertex, TestShaderPath::Vertex);
    //   _program.addShaderFile(GPUProgram::Fragment, TestShaderPath::Fragment);

    _program.addShaderFile(GPUProgram::Vertex, FiberDrawShaderPath::Vertex);
    _program.addShaderFile(GPUProgram::Geometry, FiberDrawShaderPath::Streamtube);
    _program.addShaderFile(GPUProgram::Fragment, FiberDrawShaderPath::Fragment);

    _program.use();

    _program.printActiveAttributes();
    _program.printActiveUniforms();

    _program.setUniform("VolumeSize",
        glm::vec3(DataSet1::kuWidth, DataSet1::kuHeight, DataSet1::kuDepth));
}


void DTTScene::_vboInit()
{
    size_t uSize;
    std::shared_ptr<char> pRaw;

    readBinaryFile(DataSet1::kFiberDataPath, pRaw, uSize);
    BufferObjectPtr pvbo(new BufferObject());
    pvbo->read(
        uSize,
        PixelFormat::PF_RGBA_32F,
        BufferObject::ARRAY,
        BufferObject::STATIC_DRAW,
        pRaw.get());
    _vao.addAttribArray(_program.getActiveAttribute("VertexPosition"), pvbo);

    readBinaryFile(DataSet1::kFiberIdxPath, pRaw, uSize);
    _pIndexBuffer = BufferObjectPtr(new BufferObject());
    _pIndexBuffer->read(
        uSize,
        PixelFormat::PF_R_32U,
        BufferObject::ELEMENT_ARRAY,
        BufferObject::STATIC_DRAW,
        pRaw.get());
}

void DTTScene::_textureInit()
{
    size_t uSize;
    std::shared_ptr<char> pRaw;
    size_t acticeID;

    acticeID = 0;
    readBinaryFile(DataSet1::kDiagnalDataPath, pRaw, uSize);
    Texture3DPtr ptex3d(new Texture3D());
    ptex3d->read(
        DataSet1::kuWidth,
        DataSet1::kuHeight,
        DataSet1::kuDepth,
        PixelFormat::PF_RGB_32F,
        pRaw.get());
    ptex3d->setActiveID(acticeID);
    _program.setUniform("TexDiagTensor", (int)acticeID);
    ptex3d->active();
    _tex3DArray.push_back(ptex3d);

    acticeID = 1;
    readBinaryFile(DataSet1::kOffDiagnalDataPath, pRaw, uSize);
    ptex3d = Texture3DPtr(new Texture3D());
    ptex3d->read(
        DataSet1::kuWidth,
        DataSet1::kuHeight,
        DataSet1::kuDepth,
        PixelFormat::PF_RGB_32F,
        pRaw.get());
    ptex3d->setActiveID(acticeID);
    _program.setUniform("TexOffDiagTensor", (int)acticeID);
    ptex3d->active();
    _tex3DArray.push_back(ptex3d);

}


void DTTScene::onSpecialKeyPressed( int key, int x, int y )
{

    switch(key)
    {
    case GLUT_KEY_UP:
        theta += angleStride; break;
    case GLUT_KEY_DOWN:
        theta -= angleStride; break;
    case GLUT_KEY_LEFT:
        alpha += angleStride; break;
    case GLUT_KEY_RIGHT:
        alpha -= angleStride; break;
    }

    setCameraPos();

    glutPostRedisplay();
}

void DTTScene::rotate( float x/*, float y, float z*/ )
{
    theta += x;
    //alpha += y;
    setCameraPos();

}
